package com.spring.theory.aop.advice;

import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

/**
 * aop 1-1 spring代理的选择
 *
 * @author H
 * @version 1.0
 * @date 2024-01-08 15:03
 */
@Slf4j
public class TestAdvice {

    public static void main(String[] args) {
        /**
         * 底层切点实现；
         * 创建切点
         */
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* test(..))");
        /**
         * 底层通知实现；
         * 准备通知
         */
        MethodInterceptor methodInterceptor = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                log.info("aop before..." + invocation.getClass());
                Object result = invocation.proceed();
                log.info("aop after..." + invocation.getClass());
                return result;
            }
        };

        /**
         * 准备切面；
         * advisor切面，更细粒度的切面，包含一个通知和切点
         * aspect切面，可以包含多个通知，多个切点
         */
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, methodInterceptor);
        /**
         * 创建代理
         *
         * Spring代理的选择实现:
         *   - proxyTargetClass=false
         *      --目标实现了接口 使用jdk代理；
         *      --目标没有实现接口 使用cglib代理
         *   - proxyTargetClass=true,总是使用cglib代理
         *
         */
        TestTarget target = new TestTarget();

        /**
         * spring提供的创建jdk、cglib动态代理的工厂方法
         */
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvisor(advisor);
        /**
         * 需要主动告诉代理工厂目标类是否实现了什么接口，以此来决定用什么代理
         */
        proxyFactory.setInterfaces(target.getClass().getInterfaces());
        // 代理选择
        proxyFactory.setProxyTargetClass(true);

        AdviceTarget proxy = (AdviceTarget) proxyFactory.getProxy();
        log.info("proxy:{}", proxy.getClass());

        proxy.test();
        proxy.test2();

    }

    interface AdviceTarget {
        void test();

        void test2();
    }

    static class TestTarget implements AdviceTarget {
        @Override
        public void test() {
            log.info("test方法运行");
        }

        @Override
        public void test2() {
            log.info("test2方法运行");
        }
    }

}
